using System;
using System.Xml;

namespace FreeFlow.Client
{
	/// <summary>A folder in an action.</summary>
	/// <example>
	/// How to start a blank form, set some values and submit the form.
	/// conn is an already logged-in <see cref="Connection"/> object
	/// <code language="c#">
	/// FreeFlow.Client.ActionPage page = new FreeFlow.Client.ActionPage();
	/// page.Connection = conn;
	/// page.StartBlankForm("Map1", "Action1");
	/// page.Form.Field("Text1").Value = "Hello world";
	/// page.Validate();
	/// page.Submit();
	/// </code>
	/// <code language="vb">
  /// Dim page As New FreeFlow.Client.ActionPage
  /// page.Connection = conn
  /// page.StartBlankForm("Map1", "Action1")
  /// page.Form.Field("Text1").Value = "Hello world"
  /// page.Validate()
  /// page.Submit()
	/// </code>
	/// </example>
	[Serializable]
	public class ActionPage : Connectable, IFolder
	{
    /// <summary>
    /// Creates a new <see cref="ActionPage"/> instance.
    /// </summary>
    public ActionPage()
    {
    }

    #region properties
    private Form form;
    /// <summary>Gets the form used for this action.</summary>
    public Form Form
    {
      get { return form; }
    }

    private string folderId;
    /// <summary>
    /// Gets the folder Id.
    /// </summary>
    public string FolderId
    {
      get { return folderId; } 
    }

    private bool confirm;
    /// <summary>
    /// Gets a value indicating whether this <see cref="ActionPage"/> is a confirm action.
    /// </summary>
    /// <value>
    /// 	<c>true</c> if a confirm action; otherwise, <c>false</c>.
    /// </value>
    public bool Confirm
    {
      get { return confirm; }
    }

    private string folderName;
    /// <summary>
    /// Gets the name of the folder.
    /// </summary>
    public string FolderName
    {
      get
      {
        return folderName;
      }
    }
    #endregion

    /// <summary>Starts a blank form. This method can only be used with user actions.</summary>
    /// <param name="processName">Name of the process.</param>
    /// <param name="actionName">Name of the action.</param>
    /// <example>
    /// How to start a blank form, set some values and submit the form.
    /// conn is an already logged-in <see cref="Connection"/> object
    /// <code language="c#">
    /// FreeFlow.Client.ActionPage page = new FreeFlow.Client.ActionPage();
    /// page.Connection = conn;
    /// page.StartBlankForm("Map1", "Action1");
    /// page.Form.Field("Text1").Value = "Hello world";
    /// page.Validate();
    /// page.Submit();
    /// </code>
    /// <code language="vb">
    /// Dim page As New FreeFlow.Client.ActionPage
    /// page.Connection = conn
    /// page.StartBlankForm("Map1", "Action1")
    /// page.Form.Field("Text1").Value = "Hello world"
    /// page.Validate()
    /// page.Submit()
    /// </code>
    /// </example>
    public void StartBlankForm(string processName, string actionName)
    {
      CheckConnection();
      
      XmlDocument doc = new XmlDocument();
      XmlElement docElem = doc.CreateElement("eActionRequest");
      docElem.SetAttribute("Map", processName);
      docElem.SetAttribute("Action", actionName);
      doc.AppendChild(docElem);

      // get the form
      FillFromResponse(Connection.SendDecoratedMsg(doc));

      // TODO - check if submitted
    }

    /// <summary>Starts a blank form. If the process contains multiple blank forms, 
    /// the one whose eAction.eActionID equals 1 will be started.</summary>
    /// <param name="processName">Name of the process.</param>
    public void StartBlankForm(string processName)
    {
      StartBlankForm(processName, string.Empty);
    }

    /// <summary>
    /// Starts an action.
    /// </summary>
    /// <param name="folderId">Folder id.</param>
    /// <param name="actionName">Name of the action.</param>
    /// <example>
    /// How to start an action, set some values and submit the form.
    /// conn is an already logged-in <see cref="Connection"/> object
    /// <code language="c#">
    /// FreeFlow.Client.ActionPage page = new FreeFlow.Client.ActionPage();
    /// page.Connection = conn;
    /// page.StartAction("folder id", "Action1");
    /// page.Form.Field("Text1").Value = "Hello world";
    /// page.Validate();
    /// page.Submit();
    /// </code>
    /// <code language="vb">
    /// Dim page As New FreeFlow.Client.ActionPage
    /// page.Connection = conn
    /// page.StartAction("folder id", "Action1")
    /// page.Form.Field("Text1").Value = "Hello world"
    /// page.Validate()
    /// page.Submit()
    /// </code>
    /// </example>
    public void StartAction(string folderId, string actionName)
    {
      XmlDocument doc = new XmlDocument();
      doc.LoadXml("<eActionRequest FolderID=\"" + folderId + 
        "\" Action=\"" + actionName + "\" />");

      // get the form
      FillFromResponse(Connection.SendDecoratedMsg(doc));
    }

    /// <summary>
    /// Fills the page from an action response.
    /// </summary>
    /// <param name="response">engine response.</param>
    public void FillFromResponse(XmlDocument response)
    {
      if (response != null)
      {
        confirm = response.DocumentElement.Name == "eActionResponse";
        folderName = response.DocumentElement.GetAttribute("Folder");

        XmlElement formNode = response.DocumentElement[Form.FormLayoutElement];
        if (formNode != null)
        {
          form = new Form(formNode, Connection);
          form.Connection = Connection;
          form.Page = this;
          form.FillValues(response.DocumentElement["FieldOutputList"]);
          form.FillUsage(response.DocumentElement["FieldUsageList"]);
        }
        FillServerData(response.DocumentElement["ServerData"]);

        folderId = response.DocumentElement.Attributes["FolderID"].Value;
        
        actionName = response.DocumentElement.Attributes["Action"].Value;
      }
    }

    private string serverData;
    private void FillServerData(XmlElement serverDataNode)
    {
      serverData = null;

      if (serverDataNode != null)
        if (serverDataNode.ChildNodes.Count > 0)
          serverData = serverDataNode.ChildNodes[0].Value;
    }

    private string actionName;

    /// <summary>Submits the action. Note, for non-confirm actions this method will not do anything since starting the action also submits it.</summary>
    public void Submit()
    {
      string chainedAction;
      string reopenFolderId;
      Submit(out chainedAction, out reopenFolderId);
    }

    /// <summary>Submits the action. Note, for non-confirm actions this method will not do anything since starting the action also submits it.</summary>
    /// <param name="chainedAction">The name of the next chained action, if there is one.</param>
    /// <param name="reopenFolderId">Indicates the folder ID to be reopened, if there is one.</param>
    public void Submit(out string chainedAction, out string reopenFolderId)
    {
      chainedAction = null;
      reopenFolderId  = null;
      
      // non-confirm actions don't need to be submitted
      if ((confirm == false) && (form == null))
        return;
      
      CheckConnection();
      
      XmlDocument doc = new XmlDocument();
      XmlElement docElem = doc.CreateElement("eSubmitRequest");
      doc.AppendChild(docElem);
      docElem.SetAttribute("FolderID", FolderId);
      docElem.SetAttribute("Action", actionName);
      docElem.InnerXml = ServerDataXML();

      // add field values
      if (form != null)
      {
        form.AddFieldInputList(docElem);
      }

      XmlDocument resp = Connection.SendDecoratedMsg(doc);
      if (resp.DocumentElement.Attributes["ChainedAction"] != null)
        chainedAction = resp.DocumentElement.GetAttribute("ChainedAction");
      if (resp.DocumentElement.Attributes["ReopenFolder"] != null)
        if (resp.DocumentElement.Attributes["ReopenFolder"].Value == "1")
          reopenFolderId = resp.DocumentElement.GetAttribute("FolderID");
      folderId = resp.DocumentElement.GetAttribute("FolderID");
      folderName = resp.DocumentElement.GetAttribute("Folder");
    }

    /// <summary>
    /// Cancels the action.
    /// </summary>
    public void Cancel()
    {
      if (Connection != null)
      {
        XmlDocument doc = new XmlDocument();
        XmlElement docElem = doc.CreateElement("eCancelRequest");
        docElem.SetAttribute("FolderID", folderId);
        docElem.SetAttribute("Action", actionName);
        docElem.InnerXml = ServerDataXML();
        doc.AppendChild(docElem);

        Connection.SendDecoratedMsg(doc);
      }
    }

    private string ServerDataXML()
    {
      return "<ServerData>" +
        "<![CDATA[" + serverData + "]]>" + 
        "</ServerData>";
    }

    /// <summary>
    /// Ensures fields in the form are valid in terms of minimum value, maximum value, maximum length etc.
    /// It is advisable to call this before <see cref="Submit()"/>. 
    /// If any fields fail to validate a <see cref="FreeFlowException"/> will be thrown.
    /// </summary>
    public void Validate()
    {
      if (form != null)
      {
        for (int i = 0; i < form.FieldCount; i++)
        {
          Field thisField = form.Field(i);
          thisField.Validate();
        }
      }
    }

    /// <summary>Refills the page from the engine.</summary>
    /// <param name="fieldUpdated">The name of the field that caused the refill to occur.</param>
    public void Refill(string fieldUpdated)
    {
      if (Connection != null)
      {
        XmlDocument doc = new XmlDocument();
        XmlElement docElem = doc.CreateElement("eRefillRequest");
        doc.AppendChild(docElem);
        docElem.SetAttribute("FolderID", FolderId);
        docElem.SetAttribute("Action", actionName);
        docElem.SetAttribute("Form", form.Name);
        docElem.SetAttribute("Updates", fieldUpdated);
        docElem.SetAttribute("Version", form.Version.ToString());
        docElem.InnerXml = ServerDataXML();
        
        // add field values
        form.AddFieldInputList(docElem);

        XmlDocument response = Connection.SendDecoratedMsg(doc);

        // refill field values
        form.FillValues(response.DocumentElement["FieldOutputList"]);
        FillServerData(response.DocumentElement["ServerData"]);
      }
    }
	}
}
